//
//  FFTProcessor.swift
//  AnyAudio
//
//  Created by hq_ly on 2025/7/1.
//


import SwiftUI
import AVFoundation
import Accelerate
import Combine
import CoreGraphics

// MARK: - 音频处理核心

/// FFT处理器
class FFTProcessor {
    static let windowSize = 2048
    static let hopSize = 512
    
    private(set) var fftSetup: vDSP.FFT<DSPSplitComplex>?
    private(set) var forwardInputBuffer: [Float]
    private(set) var forwardOutputReal: [Float]
    private(set) var forwardOutputImag: [Float]
    private let hanningWindow: [Float]
    
    init() {
        self.forwardInputBuffer = [Float](repeating: 0, count: Self.windowSize)
        self.forwardOutputReal = [Float](repeating: 0, count: Self.windowSize)
        self.forwardOutputImag = [Float](repeating: 0, count: Self.windowSize)
        
        // 正确创建汉宁窗
        var window = [Float](repeating: 0, count: Self.windowSize)
        vDSP_hann_window(&window, vDSP_Length(Self.windowSize), Int32(0))
        self.hanningWindow = window
        
        let log2n = vDSP_Length(log2(Float(Self.windowSize)))
        self.fftSetup = vDSP.FFT(
            log2n: log2n,
            radix: .radix2,
            ofType: DSPSplitComplex.self
        )!
    }
    
    /// 执行FFT分析
    func process(_ buffer: AVAudioPCMBuffer) -> [[Float]] {
        guard let floatData = buffer.floatChannelData?[0] else { return [] }
        let frameCount = Int(buffer.frameLength)
        
        var results: [[Float]] = []
        
        for i in stride(from: 0, to: frameCount, by: FFTProcessor.hopSize) {
            let endIndex = min(i + FFTProcessor.windowSize, frameCount)
            let length = endIndex - i
            
            // 应用汉宁窗
            var windowed = [Float](repeating: 0, count: FFTProcessor.windowSize)
            applyHanningWindow(input: floatData, start: i, length: length, output: &windowed)
            
            // 执行FFT
            performFFT(on: windowed)
            
            // 计算幅度谱 (只取前一半)
            var magnitudes = [Float](repeating: 0, count: FFTProcessor.windowSize / 2)
            computeMagnitudes(output: &magnitudes)
            
            // 保存结果
            results.append(magnitudes)
        }
        
        return results
    }
    
    /// 应用汉宁窗
    private func applyHanningWindow(input: UnsafePointer<Float>, start: Int, length: Int, output: inout [Float]) {
        // 确保长度大于0，避免无效的vDSP调用
        guard length > 0 else {
            // 如果长度为0，只需填充0
            output = [Float](repeating: 0, count: FFTProcessor.windowSize)
            return
        }
        
        output.withUnsafeMutableBufferPointer { outputPtr in
            // 确保有足够的空间
            let actualLength = min(length, FFTProcessor.windowSize)
            
            // 复制数据并应用窗函数
            vDSP_vmul(
                input.advanced(by: start), 1,
                hanningWindow, 1,
                outputPtr.baseAddress!, 1,
                vDSP_Length(actualLength))
            
            // 用零填充剩余部分
            if actualLength < FFTProcessor.windowSize {
                for i in actualLength..<FFTProcessor.windowSize {
                    outputPtr[i] = 0
                }
            }
        }
    }
    
    /// 执行FFT
    private func performFFT(on input: [Float]) {
        forwardInputBuffer = input
        
        // 准备输入复数格式 (虚部全为0)
        var realBuffer = forwardInputBuffer
        var imagBuffer = [Float](repeating: 0, count: forwardInputBuffer.count)
        
        var inputSplitComplex = DSPSplitComplex(
            realp: &realBuffer,
            imagp: &imagBuffer
        )
        
        // 准备输出缓冲区
        var outputSplitComplex = DSPSplitComplex(
            realp: &forwardOutputReal,
            imagp: &forwardOutputImag
        )
        
        // 执行FFT
        fftSetup?.forward(input: inputSplitComplex, output: &outputSplitComplex)
    }
    
    /// 计算幅度
    private func computeMagnitudes(output: inout [Float]) {
        for i in 0..<FFTProcessor.windowSize / 2 {
            let real = forwardOutputReal[i]
            let imag = forwardOutputImag[i]
            let magnitude = sqrt(real * real + imag * imag)
            output[i] = magnitude
        }
    }
}

// MARK: - 特征提取器

class AudioFeatureExtractor: ObservableObject {
    @Published var beatConfidences: [Double] = []
    @Published var spectralConfidences: [Double] = []
    @Published var harmonicConfidences: [Double] = []
    @Published var combinedConfidences: [Double] = []
    @Published var timeStamps: [Double] = [] // 时间标记
    
    private var engine = AVAudioEngine()
    private var playerNode = AVAudioPlayerNode()
    private var audioFile: AVAudioFile?
    private let fftProcessor = FFTProcessor()
    private var sampleRate: Double = 44100.0 // 默认采样率
    
    private let beatWeight: Double = 0.5
    private let spectralWeight: Double = 0.3
    private let harmonicWeight: Double = 0.2
    
    private var processingQueue = DispatchQueue(label: "audio.processing.queue", qos: .userInitiated)
    private var isProcessing = false
    private var processedFrameCount = 0
    
    /// 加载并处理音频
    func loadAndProcessAudio(_ url: URL) {
        reset()
        isProcessing = true
        
        do {
            audioFile = try AVAudioFile(forReading: url)
            
            // 获取实际采样率
            sampleRate = audioFile?.fileFormat.sampleRate ?? 44100.0
            
            // 设置音频引擎
            engine.attach(playerNode)
            engine.connect(playerNode, to: engine.mainMixerNode, format: audioFile?.processingFormat)
            
            // 安装Tap获取音频数据
            let format = engine.mainMixerNode.outputFormat(forBus: 0)
            let bufferSize: AVAudioFrameCount = AVAudioFrameCount(FFTProcessor.windowSize * 2)
            
            engine.mainMixerNode.installTap(
                onBus: 0,
                bufferSize: bufferSize,
                format: format
            ) { [weak self] buffer, _ in
                self?.processAudioBuffer(buffer)
            }
            
            // 准备播放器
            playerNode.scheduleFile(audioFile!, at: nil) { [weak self] in
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    self?.isProcessing = false
                    self?.finalizeProcessing()
                }
            }
            
            // 启动引擎
            try engine.start()
            playerNode.play()
            
        } catch {
            print("Audio processing error: \(error)")
            isProcessing = false
        }
    }
    
    /// 处理音频缓冲区
    private func processAudioBuffer(_ buffer: AVAudioPCMBuffer) {
        guard isProcessing, buffer.frameLength > 0 else { return }
        
        processingQueue.async { [weak self] in
            guard let self = self, buffer.frameLength > 0 else { return }
            
            // 处理缓冲区
            let spectrums = self.fftProcessor.process(buffer)
            
            // 如果没有任何频谱数据，跳过处理
            guard !spectrums.isEmpty else { return }
            
            var beatResults: [Double] = []
            var spectralResults: [Double] = []
            var harmonicResults: [Double] = []
            var combinedResults: [Double] = []
            var timeResults: [Double] = [] // 时间标记
            
            // 计算每个频谱的时间点
            let hopTime = Double(FFTProcessor.hopSize) / self.sampleRate
            let startIndex = self.processedFrameCount
            
            for (index, spectrum) in spectrums.enumerated() {
                // 计算时间点 (基于帧索引)
                let timeStamp = Double(startIndex + index) * hopTime
                timeResults.append(timeStamp)
                
                // 计算短时能量（节拍置信度）
                let beatConfidence = self.calculateBeatConfidence(from: spectrum)
                beatResults.append(beatConfidence)
                
                // 计算频谱质心
                let spectralConfidence = self.calculateSpectralCentroid(from: spectrum)
                spectralResults.append(spectralConfidence)
                
                // 计算谐波冲击度
                let harmonicConfidence = self.calculateHarmonicContrast(from: spectrum)
                harmonicResults.append(harmonicConfidence)
                
                // 融合特征
                let combined = beatConfidence * self.beatWeight + 
                               spectralConfidence * self.spectralWeight + 
                               harmonicConfidence * self.harmonicWeight
                combinedResults.append(combined)
            }
            
            DispatchQueue.main.async {
                self.beatConfidences.append(contentsOf: beatResults)
                self.spectralConfidences.append(contentsOf: spectralResults)
                self.harmonicConfidences.append(contentsOf: harmonicResults)
                self.combinedConfidences.append(contentsOf: combinedResults)
                self.timeStamps.append(contentsOf: timeResults)
            }
            
            self.processedFrameCount += spectrums.count
        }
    }
    
    /// 完成处理
    private func finalizeProcessing() {
        processingQueue.async { [weak self] in
            guard let self = self, !self.combinedConfidences.isEmpty else { return }
            
            // 应用移动平均平滑
            let smoothKernel: [Double] = [0.1, 0.2, 0.4, 0.2, 0.1]
            let smoothedCombined = self.applyConvolution(input: self.combinedConfidences, kernel: smoothKernel)
            
            DispatchQueue.main.async {
                self.combinedConfidences = smoothedCombined
            }
        }
    }
    
    /// 计算节拍置信度
    private func calculateBeatConfidence(from spectrum: [Float]) -> Double {
        // 计算总能量
        var totalEnergy: Double = 0.0
        var prevEnergy: Double = 0.0
        var flux: Double = 0.0
        
        for i in 0..<spectrum.count {
            let bin = Double(spectrum[i])
            totalEnergy += bin
            
            if i > 0 {
                flux += abs(bin - prevEnergy)
            }
            
            prevEnergy = bin
        }
        
        // 标准化
        let normalizedFlux = flux / max(1, Double(spectrum.count))
        let normalizedEnergy = totalEnergy / max(1, Double(spectrum.count))
        
        // 组合指标
        let confidence = (normalizedFlux * 0.7) + (normalizedEnergy * 0.3)
        return min(1.0, max(0.0, confidence))
    }
    
    /// 计算频谱质心
    private func calculateSpectralCentroid(from spectrum: [Float]) -> Double {
        var sumWeighted = 0.0
        var sum = 0.0
        
        for i in 0..<spectrum.count {
            let bin = Double(spectrum[i])
            let frequency = Double(i) / Double(spectrum.count)
            
            sumWeighted += bin * frequency
            sum += bin
        }
        
        let centroid = sum > 0 ? sumWeighted / sum : 0.0
        // 将质心映射到0-1范围
        return min(1.0, max(0.0, centroid * 1.5))
    }
    
    /// 计算谐波冲击度
    private func calculateHarmonicContrast(from spectrum: [Float]) -> Double {
        // 分离谐波和打击乐成分
        var harmonicPart: Double = 0.0
        var percussivePart: Double = 0.0
        
        let binGroupSize = max(4, spectrum.count / 40)
        
        for groupStart in stride(from: 0, to: spectrum.count, by: binGroupSize) {
            let groupEnd = min(groupStart + binGroupSize, spectrum.count)
            var localMax: Double = 0.0
            var groupSum: Double = 0.0
            
            for bin in groupStart..<groupEnd {
                let value = Double(spectrum[bin])
                groupSum += value
                if value > localMax {
                    localMax = value
                }
            }
            
            harmonicPart += groupSum
            percussivePart += localMax
        }
        
        // 计算谐波对比度
        let contrast = harmonicPart > 0 ? percussivePart / harmonicPart : 0.0
        return min(1.0, max(0.0, contrast * 0.8))
    }
    
    /// 应用卷积平滑
    private func applyConvolution(input: [Double], kernel: [Double]) -> [Double] {
        let kernelSize = kernel.count
        let halfKernelSize = kernelSize / 2
        var output = [Double](repeating: 0, count: input.count)
        
        for i in 0..<input.count {
            var convValue = 0.0
            for j in 0..<kernelSize {
                let idx = i + j - halfKernelSize
                if idx >= 0 && idx < input.count {
                    convValue += input[idx] * kernel[j]
                }
            }
            output[i] = convValue
        }
        
        return output
    }
    
    /// 重置状态
    private func reset() {
        beatConfidences = []
        spectralConfidences = []
        harmonicConfidences = []
        combinedConfidences = []
        timeStamps = []
        processedFrameCount = 0
    }
}

// MARK: - 峰值检测器

class PeakDetector: ObservableObject {
    @Published var detectedPeaks: [Int] = []
    @Published var peakTimes: [Double] = [] // 峰值对应的时间点
    @Published var confidenceHistory: [Double] = []
    
    private let minPeakHeight: Double = 0.5
    private let minPeakDistance: Int = 5 // 最小峰值距离（单位：帧）
    
    /// 在数据中检测峰值
    func detectPeaks(in confidenceData: [Double], timeStamps: [Double]) {
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            guard let self = self else { return }
            
            self.confidenceHistory = confidenceData
            
            var peaks: [Int] = []
            var peakTimes: [Double] = [] // 存储峰值对应的时间
            let data = confidenceData
            
            // 如果数据点太少，直接返回
            if data.count < 2 {
                DispatchQueue.main.async {
                    self.detectedPeaks = []
                    self.peakTimes = []
                }
                return
            }
            
            // 滑动窗口峰值检测算法
            let windowSize = minPeakDistance
            for i in 0..<data.count {
                // 跳过边界点
                if i < windowSize || i >= data.count - windowSize {
                    continue
                }
                
                // 检查高度条件
                guard data[i] > self.minPeakHeight else { continue }
                
                // 检查局部最大值条件
                var isPeak = true
                let leftStart = max(0, i - windowSize)
                let rightEnd = min(data.count - 1, i + windowSize)
                
                for j in leftStart...rightEnd {
                    if i != j && data[j] > data[i] {
                        isPeak = false
                        break
                    }
                }
                
                // 检查最小距离条件
                if isPeak {
                    if let lastPeak = peaks.last, i - lastPeak < self.minPeakDistance {
                        // 保留更高的峰值
                        if data[i] > data[lastPeak] {
                            peaks[peaks.count - 1] = i
                            peakTimes[peakTimes.count - 1] = timeStamps[i]
                        }
                    } else {
                        peaks.append(i)
                        peakTimes.append(timeStamps[i])
                    }
                }
            }
            
            DispatchQueue.main.async {
                self.detectedPeaks = peaks
                self.peakTimes = peakTimes
            }
        }
    }
}

// MARK: - 可视化视图（带横向滚动和缩放）

struct MultiFeatureBeatVisualizer: View {
    @StateObject private var featureExtractor = AudioFeatureExtractor()
    @StateObject private var peakDetector = PeakDetector()
    @State private var audioURL: URL?
    @State private var isProcessing = false
    @State private var processingProgress: Double = 0.0
    @State private var selectedBand: FrequencyBand = .all
    
    // 新增状态变量用于滚动和缩放
    @State private var zoomScale: CGFloat = 1.0
    @State private var scrollOffset: CGFloat = 0.0
    @State private var contentWidth: CGFloat = 1000
    @State private var isDragging = false
    @State private var dragStartOffset: CGFloat = 0.0
    
    // 波段选择
    enum FrequencyBand: String, CaseIterable, Identifiable {
        case all = "全部"
        case low = "低频"
        case mid = "中频"
        case high = "高频"
        
        var id: String { self.rawValue }
    }
    
    // 可视化参数
    private let barWidth: CGFloat = 2.0
    private let barSpacing: CGFloat = 1.0
    private let waveHeight: CGFloat = 200
    
    var body: some View {
        VStack(spacing: 20) {
            // 标题和控制区域
            VStack(spacing: 10) {
                Text("多特征融合卡点分析")
                    .font(.title)
                    .fontWeight(.bold)
                
                HStack(spacing: 20) {
                    Button(action: selectAudioFile) {
                        HStack {
                            Image(systemName: "waveform.path")
                            Text("选择音频")
                        }
                        .padding(10)
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(10)
                    }
                    
                    if isProcessing {
                        ProgressView(value: processingProgress)
                            .progressViewStyle(LinearProgressViewStyle(tint: .blue))
                            .frame(width: 200)
                    }
                    
                    // 波段选择器
                    if !featureExtractor.combinedConfidences.isEmpty {
                        Picker("频率波段", selection: $selectedBand) {
                            ForEach(FrequencyBand.allCases) { band in
                                Text(band.rawValue).tag(band)
                            }
                        }
                        .pickerStyle(SegmentedPickerStyle())
                        .frame(width: 200)
                    }
                }
                
                if !featureExtractor.combinedConfidences.isEmpty {
                    Text("检测到 \(peakDetector.detectedPeaks.count) 个卡点位置")
                        .font(.headline)
                }
            }
            .padding()
            
            // 缩放控制
            if !featureExtractor.combinedConfidences.isEmpty {
                HStack {
                    Text("缩放: \(zoomScale, specifier: "%.1f")x")
                        .font(.caption)
                    
                    Slider(value: $zoomScale, in: 0.1...5.0, step: 0.1) {
                        Text("缩放控制")
                    }
                    .frame(maxWidth: 200)
                    
                    Button(action: resetZoom) {
                        Text("重置视图")
                            .padding(.horizontal, 10)
                            .padding(.vertical, 5)
                            .background(Color.gray.opacity(0.2))
                            .cornerRadius(8)
                    }
                }
                .padding(.horizontal, 40)
            }
            
            // 波形可视化（带滚动和缩放）
            GeometryReader { geometry in
                let availableWidth = geometry.size.width
                let scrollableWidth = max(0, contentWidth * zoomScale - availableWidth)
                
                VStack(spacing: 10) {
                    // 滚动条指示器
                    if scrollableWidth > 0 {
                        ScrollIndicator(
                            totalWidth: contentWidth * zoomScale,
                            visibleWidth: availableWidth,
                            offset: scrollOffset,
                            height: 10
                        )
                        .padding(.horizontal, 10)
                    }
                    
                    // 滚动视图
                    ScrollView(.horizontal, showsIndicators: false) {
                        ZStack {
                            // 背景网格
                            GridBackgroundView(
                                width: contentWidth * zoomScale,
                                height: waveHeight,
                                horizontalLines: 10,
                                verticalLines: 20
                            )
                            
                            // 特征叠加图
                            FeatureVisualization(
                                beatValues: scaledBeatConfidences,
                                spectralValues: scaledSpectralConfidences,
                                harmonicValues: scaledHarmonicConfidences,
                                combinedValues: scaledCombinedConfidences,
                                height: waveHeight,
                                width: contentWidth * zoomScale
                            )
                            
                            // 检测到的峰值
                            PeaksOverlay(
                                peaks: peakDetector.detectedPeaks,
                                peakTimes: peakDetector.peakTimes,
                                combinedValues: scaledCombinedConfidences,
                                height: waveHeight,
                                totalWidth: contentWidth * zoomScale,
                                dataCount: featureExtractor.combinedConfidences.count
                            )
                        }
                        .frame(width: contentWidth * zoomScale, height: waveHeight)
                        .background(Color.black.opacity(0.1))
                        .cornerRadius(8)
                        .offset(x: scrollOffset)
                        .gesture(
                            DragGesture()
                                .onChanged { value in
                                    if !isDragging {
                                        isDragging = true
                                        dragStartOffset = scrollOffset
                                    }
                                    
                                    let newOffset = dragStartOffset + value.translation.width
                                    scrollOffset = min(0, max(-scrollableWidth, newOffset))
                                }
                                .onEnded { _ in
                                    isDragging = false
                                }
                        )
                    }
                    
                    // 特征图例
                    HStack {
                        HStack {
                            Rectangle()
                                .fill(Color.red)
                                .frame(width: 15, height: 3)
                            Text("节拍强度")
                                .font(.caption)
                        }
                        HStack {
                            Rectangle()
                                .fill(Color.green)
                                .frame(width: 15, height: 3)
                            Text("频谱质心")
                                .font(.caption)
                        }
                        HStack {
                            Rectangle()
                                .fill(Color.blue)
                                .frame(width: 15, height: 3)
                            Text("谐波冲击")
                                .font(.caption)
                        }
                        HStack {
                            Rectangle()
                                .fill(Color.purple)
                                .frame(width: 15, height: 3)
                            Text("融合特征")
                                .font(.caption)
                        }
                    }
                    .padding(.vertical, 5)
                }
                .padding(.horizontal, 10)
                .onChange(of: zoomScale) { _ in
                    // 缩放时调整滚动位置以保持中心
                    let centerRatio = scrollableWidth > 0 ? -scrollOffset / scrollableWidth : 0
                    let newScrollableWidth = max(0, contentWidth * zoomScale - availableWidth)
                    scrollOffset = -centerRatio * newScrollableWidth
                }
                .onChange(of: featureExtractor.combinedConfidences) { _ in
                    // 数据变化时更新内容宽度
                    updateContentWidth()
                }
                .onAppear {
                    // 初始内容宽度基于数据量
                    updateContentWidth()
                }
            }
            .padding(.horizontal, 20)
            .frame(height: waveHeight + 50) // 增加高度以容纳滚动条
            
            // 详细特征展示
            if !featureExtractor.combinedConfidences.isEmpty {
                HStack(spacing: 0) {
                    DetailFeatureChart(
                        title: "节拍强度",
                        data: scaledBeatConfidences,
                        color: .red,
                        min: 0,
                        max: 1.0
                    )
                    
                    DetailFeatureChart(
                        title: "频谱质心",
                        data: scaledSpectralConfidences,
                        color: .green,
                        min: 0,
                        max: 1.0
                    )
                    
                    DetailFeatureChart(
                        title: "谐波冲击",
                        data: scaledHarmonicConfidences,
                        color: .blue,
                        min: 0,
                        max: 1.0
                    )
                }
                .frame(height: 150)
                .padding(.horizontal, 20)
            }
            
            Spacer()
        }
        .onChange(of: featureExtractor.combinedConfidences) { newValue in
            if !newValue.isEmpty {
                peakDetector.detectPeaks(in: newValue, timeStamps: featureExtractor.timeStamps)
                isProcessing = false
            }
        }
        .onChange(of: selectedBand) { _ in
            // 波段变化时更新视图
        }
        .onAppear {
            // 开发测试时自动加载示例
            #if DEBUG
            if let demoURL = Bundle.main.url(forResource: "demo_audio", withExtension: "wav") {
                processAudio(url: demoURL)
            }
            #endif
        }
    }
    
    /// 更新内容宽度
    private func updateContentWidth() {
        if featureExtractor.combinedConfidences.isEmpty {
            contentWidth = 1000
        } else {
            // 每个数据点分配4点宽度
            contentWidth = CGFloat(featureExtractor.combinedConfidences.count) * 4
        }
    }
    
    /// 重置缩放和滚动位置
    private func resetZoom() {
        withAnimation(.spring()) {
            zoomScale = 1.0
            scrollOffset = 0.0
        }
    }
    
    // 波段缩放后的特征数据
    private var scaledBeatConfidences: [Double] {
        applyBandScaling(featureExtractor.beatConfidences)
    }
    
    private var scaledSpectralConfidences: [Double] {
        applyBandScaling(featureExtractor.spectralConfidences)
    }
    
    private var scaledHarmonicConfidences: [Double] {
        applyBandScaling(featureExtractor.harmonicConfidences)
    }
    
    private var scaledCombinedConfidences: [Double] {
        applyBandScaling(featureExtractor.combinedConfidences)
    }
    
    /// 应用波段缩放
    private func applyBandScaling(_ data: [Double]) -> [Double] {
        guard !data.isEmpty else { return [] }
        
        // 根据选择的波段缩放数据
        switch selectedBand {
        case .low:
            return data.map { $0 * 0.8 }
        case .mid:
            return data.map { $0 * 1.2 }
        case .high:
            return data.map { $0 * 1.5 }
        case .all:
            return data
        }
    }
    
    /// 选择音频文件
    private func selectAudioFile() {
        #if os(macOS)
        openFilePanel()
        #else
        // 在iOS上，需要实现一个真正的文件选择器
        // 这里使用测试音频代替
        if let testURL = Bundle.main.url(forResource: "GG", withExtension: "wav") {
            processAudio(url: testURL)
        }
        #endif
    }
    
    #if os(macOS)
    /// macOS文件选择器
    private func openFilePanel() {
        let panel = NSOpenPanel()
        panel.allowedContentTypes = [.audio]
        panel.allowsMultipleSelection = false
        
        if panel.runModal() == .OK, let url = panel.url {
            processAudio(url: url)
        }
    }
    #endif
    
    /// 处理音频
    private func processAudio(url: URL) {
        isProcessing = true
        processingProgress = 0.0
        audioURL = url
        zoomScale = 1.0
        scrollOffset = 0.0
        featureExtractor.loadAndProcessAudio(url)
        
        // 模拟处理进度
        Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { timer in
            if processingProgress >= 1.0 {
                timer.invalidate()
            } else {
                withAnimation {
                    processingProgress = min(1.0, processingProgress + 0.01)
                }
            }
        }
    }
}

// MARK: - 自定义视图组件

/// 滚动指示器
struct ScrollIndicator: View {
    let totalWidth: CGFloat
    let visibleWidth: CGFloat
    let offset: CGFloat
    let height: CGFloat
    
    var body: some View {
        GeometryReader { geometry in
            let containerWidth = geometry.size.width
            let indicatorWidth = max(20, containerWidth * (visibleWidth / totalWidth))
            let indicatorPosition = -offset / totalWidth * containerWidth
            
            ZStack(alignment: .leading) {
                Capsule()
                    .fill(Color.gray.opacity(0.3))
                    .frame(height: height / 2)
                
                Capsule()
                    .fill(Color.blue)
                    .frame(width: indicatorWidth, height: height)
                    .offset(x: indicatorPosition)
                    .animation(.easeInOut, value: indicatorPosition)
            }
        }
        .frame(height: height)
    }
}

/// 网格背景视图
struct GridBackgroundView: View {
    let width: CGFloat
    let height: CGFloat
    let horizontalLines: Int
    let verticalLines: Int
    
    var body: some View {
        ZStack {
            // 水平网格线
            ForEach(0..<horizontalLines, id: \.self) { i in
                Path { path in
                    let y = height * CGFloat(i) / CGFloat(horizontalLines - 1)
                    path.move(to: CGPoint(x: 0, y: y))
                    path.addLine(to: CGPoint(x: width, y: y))
                }
                .stroke(Color.gray.opacity(0.2), lineWidth: 0.5)
            }
            
            // 垂直网格线
            ForEach(0..<verticalLines, id: \.self) { i in
                Path { path in
                    let x = width * CGFloat(i) / CGFloat(verticalLines - 1)
                    path.move(to: CGPoint(x: x, y: 0))
                    path.addLine(to: CGPoint(x: x, y: height))
                }
                .stroke(Color.gray.opacity(0.2), lineWidth: 0.5)
            }
        }
    }
}

/// 特征可视化视图
struct FeatureVisualization: View {
    let beatValues: [Double]
    let spectralValues: [Double]
    let harmonicValues: [Double]
    let combinedValues: [Double]
    let height: CGFloat
    let width: CGFloat
    
    var body: some View {
        ZStack {
            // 节拍置信度
            ConfidencePath(values: beatValues, color: .red, height: height, width: width)
            
            // 频谱质心
            ConfidencePath(values: spectralValues, color: .green, height: height, width: width)
            
            // 谐波冲击度
            ConfidencePath(values: harmonicValues, color: .blue, height: height, width: width)
            
            // 融合置信度
            ConfidencePath(values: combinedValues, color: .purple, height: height, width: width)
        }
    }
}

/// 置信度路径视图
struct ConfidencePath: View {
    let values: [Double]
    let color: Color
    let height: CGFloat
    let width: CGFloat
    
    var body: some View {
        Path { path in
            guard !values.isEmpty else { return }
            
            // 确保宽度大于0，避免除以0
            let stepX = width / max(1, CGFloat(values.count))
            
            // 移动到第一个点
            let startConfidence = max(0.0, min(1.0, CGFloat(values[0])))
            let startY = height - startConfidence * height
            path.move(to: CGPoint(x: 0, y: startY))
            
            // 连接后续点
            for i in 1..<values.count {
                let confidence = max(0.0, min(1.0, CGFloat(values[i])))
                let y = height - confidence * height
                let x = CGFloat(i) * stepX
                path.addLine(to: CGPoint(x: x, y: y))
            }
        }
        .stroke(color, style: StrokeStyle(lineWidth: 1.5, lineCap: .round, lineJoin: .round))
    }
}

/// 峰值叠加视图
struct PeaksOverlay: View {
    let peaks: [Int]
    let peakTimes: [Double]
    let combinedValues: [Double]
    let height: CGFloat
    let totalWidth: CGFloat
    let dataCount: Int
    
    var body: some View {
        ForEach(0..<peaks.count, id: \.self) { idx in
            let index = peaks[idx]
            let time = peakTimes[idx]
            
            if index < combinedValues.count {
                let positionX = CGFloat(index) / CGFloat(dataCount) * totalWidth
                let confidence = combinedValues[index]
                let y = height - CGFloat(confidence) * height
                
                VStack(spacing: 2) {
                    Circle()
                        .fill(Color.yellow)
                        .frame(width: 12, height: 12)
                        .overlay(
                            Circle()
                                .stroke(Color.white, lineWidth: 2)
                        )
                        .shadow(color: .yellow, radius: 5)
                    
                    Text("\(time, specifier: "%.2f")s")
                        .font(.system(size: 10))
                        .padding(2)
                        .background(Color.black.opacity(0.5))
                        .cornerRadius(3)
                        .foregroundColor(.white)
                }
                .position(x: positionX, y: y - 20)
            }
        }
    }
}

// MARK: - 详细特征图表
struct DetailFeatureChart: View {
    let title: String
    let data: [Double]
    let color: Color
    let min: Double
    let max: Double

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .topLeading) {
                // 背景网格
                Path { path in
                    for i in 0..<5 {
                        let y = geometry.size.height * CGFloat(i) / 4
                        path.move(to: CGPoint(x: 0, y: y))
                        path.addLine(to: CGPoint(x: geometry.size.width, y: y))
                    }
                }
                .stroke(Color.gray.opacity(0.2), lineWidth: 1)

                // 数据路径
                Path { path in
                    guard !data.isEmpty else { return }

                    let height = geometry.size.height
                    let width = geometry.size.width
                    let stepX = width / CGFloat(data.count)

                    let normalized = normalizedValue(for: data[0])
                    let startY = height - normalized * height
                    path.move(to: CGPoint(x: 0, y: startY))

                    for i in 1..<data.count {
                        let x = CGFloat(i) * stepX
                        let normalized = normalizedValue(for: data[i])
                        let y = height - normalized * height
                        path.addLine(to: CGPoint(x: x, y: y))
                    }
                }
                .stroke(color, lineWidth: 2)

                // 区域填充
                Path { path in
                    guard !data.isEmpty else { return }

                    let height = geometry.size.height
                    let width = geometry.size.width
                    let stepX = width / CGFloat(data.count)

                    path.move(to: CGPoint(x: 0, y: height))

                    for i in 0..<data.count {
                        let x = CGFloat(i) * stepX
                        let normalized = normalizedValue(for: data[i])
                        let y = height - normalized * height
                        path.addLine(to: CGPoint(x: x, y: y))
                    }

                    path.addLine(to: CGPoint(x: width, y: height))
                    path.closeSubpath()
                }
                .fill(color.opacity(0.2))

                // 标题
                Text(title)
                    .font(.caption)
                    .fontWeight(.bold)
                    .foregroundColor(color)
                    .padding(3)
                    .background(RoundedRectangle(cornerRadius: 3).fill(Color.white.opacity(0.7)))
                    .offset(x: 5, y: 5)
            }
        }
        .padding(3)
    }

    private func normalizedValue(for value: Double) -> CGFloat {
        let range = max - min
        let normalized = (value - min) / range
        return CGFloat(normalized)
    }
}
    
//struct DetailFeatureChart: View {
//    let title: String
//    let data: [Double]
//    let color: Color
//    let min: Double
//    let max: Double
//    
//    var body: some View {
//        GeometryReader { geometry in
//            ZStack(alignment: .topLeading) {
//                // 背景网格
//                Path { path in
//                    for i in 0..<5 {
//                        let y = geometry.size.height * CGFloat(i) / 4
//                        path.move(to: CGPoint(x: 0, y: y))
//                        path.addLine(to: CGPoint(x: geometry.size.width, y: y))
//                    }
//                }
//                .stroke(Color.gray.opacity(0.2), lineWidth: 1)
//                
//                // 数据路径
//                Path { path in
//                    guard !data.isEmpty else { return }
//                    
//                    let height = geometry.size.height
//                    let width = geometry.size.width
//                    let stepX = width / max(1, CGFloat(data.count))
//                    
//                    let normalized = normalizedValue(for: data[0])
//                    let startY = height - normalized * height
//                    path.move(to: CGPoint(x: 0, y: startY))
//                    
//                    for i in 1..<data.count {
//                        let x = CGFloat(i) * stepX
//                        let normalized = normalizedValue(for: data[i])
//                        let y = height - normalized * height
//                        path.addLine(to: CGPoint(x: x, y: y))
//                    }
//                }
//                .stroke(color, lineWidth: 2)
//                
//                // 区域填充
//                Path { path in
//                    guard !data.isEmpty else { return }
//                    
//                    let height = geometry.size.height
//                    let width = geometry.size.width
//                    let stepX = width / max(1, CGFloat(data.count))
//                    
//                    path.move(to: CGPoint(x: 0, y: height))
//                    
//                    for i in 0..<data.count {
//                        let x = CGFloat(i) * stepX
//                        let normalized = normalizedValue(for: data[i])
//                        let y = height - normalized * height
//                        path.addLine(to: CGPoint(x: x, y: y))
//                    }
//                    
//                    path.addLine(to: CGPoint(x: width, y: height))
//                    path.closeSubpath()
//                }
//                .fill(color.opacity(0.2))
//                
//                // 标题
//                Text(title)
//                    .font(.caption)
//                    .fontWeight(.bold)
//                    .foregroundColor(color)
//                    .padding(3)
//                    .background(RoundedRectangle(cornerRadius: 3).fill(Color.white.opacity(0.7)))
//                    .offset(x: 5, y: 5)
//            }
//        }
//        .padding(3)
//    }
//    
//    private func normalizedValue(for value: Double) -> CGFloat {
//        let range = max - min
//        guard range > 0 else { return 0 }
//        let normalized = (value - min) / range
//        return CGFloat(normalized)
//    }
//}

// MARK: - 应用主入口

//@main
//struct MultiFeatureBeatAnalyzerApp: App {
//    var body: some Scene {
//        WindowGroup {
//            MultiFeatureBeatVisualizer()
//                .frame(minWidth: 800, minHeight: 600)
//        }
//    }
//}
